package org.noote.libs.sogle.helper;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;

import org.noote.libs.sogle.Material;
import org.noote.libs.sogle.Mesh;
import org.noote.libs.sogle.Object3D;
import org.noote.libs.sogle.Object3D_Group;

public class AsciiFormat_Loader {

	static final int MODE_HEADER=0, MODE_MATERIALCOUNT=1, MODE_MATERIAL_NAME=2, MODE_MATERIAL_DEF=3, MODE_OBJECT_COUNT=4, MODE_OBJECT_NAME=5, MODE_OBJECT_DEF=6, MODE_VERTEX_DEF=7, MODE_FACE_DEF=8, MODE_END=9;
	
	static public Object3D load(InputStream fis)
	{
		return load(fis, false);
	}
	static public Object3D load(InputStream fis, boolean bForceNormales)
	{
		Object3D_Group group = new Object3D_Group();
		ArrayList<Material> materials = new ArrayList<Material>();
		
		Material mat = null;
		float[] vertices = null, normales = null;
		short[] indices = null;
		int iMeshMatIndex = -1;
		
		int mode = MODE_HEADER;
		int inc_mat = 0, inc_mat_type=0, inc_mesh = 0, inc_vertex = 0, inc_face=0;
		int iMat = 0, iMesh = 0, iVertex = 0, iFace=0;

		String sLine;
		InputStreamReader isr = new InputStreamReader(fis);
		BufferedReader reader = new BufferedReader(isr, 1024);
		try {
			
			while (mode!=MODE_END && (sLine = reader.readLine()) != null) {
				sLine = sLine.trim();
				if (sLine.length() > 0) {

					switch( mode )
					{
						case MODE_HEADER:
						{
							String sSplit[] = sLine.split("\\ ");
							if(sSplit.length != 2) return null;
							if(!sSplit[0].equalsIgnoreCase("Sogle") || !sSplit[1].equalsIgnoreCase("1")) return null;
							mode++;
							break;
						}
						case MODE_MATERIALCOUNT:
						{
							iMat = Integer.parseInt(sLine);
							if(iMat<=0) return null;
							inc_mat = 0;
							mode++;
							break;
						}
						case MODE_MATERIAL_NAME:
						{
							mat = new Material();
							materials.add(mat);
							inc_mat_type=0;
							mode++;
							break;
						}
						case MODE_MATERIAL_DEF:
						{
							String sArg[] = sLine.split("\\ ");
							if(inc_mat_type==0)
							{
								if(sArg.length != 4)
									return null;
								mat.setAmbient(Float.parseFloat(sArg[0]), Float.parseFloat(sArg[1]), Float.parseFloat(sArg[2]), Float.parseFloat(sArg[3]));
							}
							else if(inc_mat_type==1)
							{
								if(sArg.length != 4)
									return null;
								mat.setDiffuse(Float.parseFloat(sArg[0]), Float.parseFloat(sArg[1]), Float.parseFloat(sArg[2]), Float.parseFloat(sArg[3]));
							}
							else if(inc_mat_type==2)
							{
								if(sArg.length != 5)
									return null;
								mat.setSpecular(Float.parseFloat(sArg[4]), Float.parseFloat(sArg[0]), Float.parseFloat(sArg[1]), Float.parseFloat(sArg[2]), Float.parseFloat(sArg[3]));
							}
							else if(inc_mat_type==3)
							{
								if(sArg.length != 4)
									return null;
								mat.setEmission(Float.parseFloat(sArg[0]), Float.parseFloat(sArg[1]), Float.parseFloat(sArg[2]), Float.parseFloat(sArg[3]));
								
								inc_mat++;
								if(inc_mat == iMat) mode=MODE_OBJECT_COUNT;
								else mode = MODE_MATERIAL_NAME;
								break;
							}
							inc_mat_type++;
							break;
						}
						case MODE_OBJECT_COUNT:
						{
							iMesh = Integer.parseInt(sLine);
							if(iMesh<=0) return null;
							inc_mesh = 0;
							mode++;
							break;
						}
						case MODE_OBJECT_NAME:
						{
							// TODO something with name ^^
							
							mode++;
							break;
						}
						case MODE_OBJECT_DEF:
						{
							String sSplit[] = sLine.split("\\ ");
							if(sSplit.length != 3) return null;
							
							iMeshMatIndex = Integer.parseInt(sSplit[0]);
							if(iMeshMatIndex>materials.size()) return null;
							
							iVertex = Integer.parseInt(sSplit[1]);
							if(iVertex <= 0) return null;
							vertices = new float [iVertex*3];
							normales = new float [iVertex*3];
							inc_vertex=0;
							
							iFace = Integer.parseInt(sSplit[2]);
							if(iFace <= 0) return null;
							indices = new short [iFace*3];
							inc_face=0;
							
							mode++;
							break;
						}
						case MODE_VERTEX_DEF:
						{
							String sSplit[] = sLine.split("\\ ");
							if(sSplit.length != 8)
								return null;
							
							int idx = inc_vertex*3;

							vertices[idx+0] = Float.parseFloat(sSplit[0]);
							vertices[idx+1] = Float.parseFloat(sSplit[1]);
							vertices[idx+2] = Float.parseFloat(sSplit[2]);
							
							normales[idx+0] = Float.parseFloat(sSplit[3]);
							normales[idx+1] = Float.parseFloat(sSplit[4]);
							normales[idx+2] = Float.parseFloat(sSplit[5]);
							
							// TODO Add Texture mapping S=sSplit[6],T=sSplit[7]
							
							inc_vertex++;
							if(inc_vertex == iVertex)
								mode++;
							
							break;
						}
						case MODE_FACE_DEF:
						{
							String sSplit[] = sLine.split("\\ ");
							if(sSplit.length != 3) return null;

							int idx = inc_face*3;
							indices[idx+0] = Short.parseShort(sSplit[0]);
							indices[idx+1] = Short.parseShort(sSplit[1]);
							indices[idx+2] = Short.parseShort(sSplit[2]);
							
							inc_face++;
							if(inc_face == iFace)
							{
								Mesh mesh = new Mesh();
								
								mesh.getMaterial().set(materials.get(iMeshMatIndex));
								mesh.setVertices(vertices);
								mesh.setIndices(indices);
								if(bForceNormales)
									mesh.generateNormales();
								else
									mesh.setVerticeNormales(normales);
								
								group.add(mesh);
							
								inc_mesh++;
								if(inc_mesh == iMesh) mode=MODE_END;
								else mode=MODE_OBJECT_NAME;
							}
							
							break;
						}
					}
				}
			}
			
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
		
		return group;
	}
}
